#include "altapplication.h"

#include "altglobals.h"
#include "altmapeditorplugin.h"
#include "altmapplugin.h"

#include <QDataStream>
#include <QFile>
#include <QPluginLoader>
#include <QSettings>

#include <QtDebug>

namespace Alt {
     QMultiHash<QString, MapEditorPlugin*> Application::m_mapEditorPluginCache = QMultiHash<QString, MapEditorPlugin*>();

     QHash<QString, MapPlugin*> Application::m_mapPluginCache = QHash<QString, MapPlugin*>();

     QHash<int, QString> Application::m_materialIdToNameHash = QHash<int, QString>();

     QHash<QString, int> Application::m_materialNameToIdHash = QHash<QString, int>();

     QList<Plugin*> Application::m_pluginCache = QList<Plugin*>();

     QDir Application::m_pluginDir = QDir();

     QObjectList Application::m_pluginObjectCache = QObjectList();

     Application::Application(int& argc, char** argv) : QApplication(argc, argv) {
          //Initialize plugin path
          m_pluginDir = QDir(applicationDirPath());
#if defined(Q_WS_WIN)
          if (m_pluginDir.dirName().toLower() == "debug" || m_pluginDir.dirName().toLower() == "release")
               m_pluginDir.cdUp();
#elif defined(Q_WS_MAC)
          if (m_pluginDir.dirName() == "MacOS") {
               m_pluginDir.cdUp();
               m_pluginDir.cdUp();
               m_pluginDir.cdUp();
          }
#elif defined(Q_WS_X11)
          m_pluginDir = QDir("/usr/lib/alt");
#endif
          m_pluginDir.cd("plugins");

          buildPluginCache();

          refreshMaterialNames();
     }

     QList<MapEditorPlugin*> Application::findMapEditorPlugins(const QString& type){
          return m_mapEditorPluginCache.values(type);
     }

     bool Application::exportMaterialNames(const QString& fileName) {
          QFile file(fileName);
          if(!file.open(QIODevice::WriteOnly)){
               return false;
          }
          refreshMaterialNames();
          QDataStream stream(&file);
          stream << QString("alt material names");
          stream << m_materialIdToNameHash;
          return true;
     }

     MapPlugin* Application::findMapPlugin(const QString& type){
          return m_mapPluginCache.value(type, 0);
     }

     const QMultiHash<QString, MapEditorPlugin*>& Application::mapEditorPlugins() {
          return m_mapEditorPluginCache;
     }

     bool Application::importMaterialNames(const QString& fileName) {
          QFile file(fileName);
          if(!file.open(QIODevice::ReadOnly)){
               return false;
          }
          QDataStream stream(&file);
          QString identifier;
          stream >> identifier;
          if (identifier != QString("alt material names")) {
               return false;
          }
          QHash<int, QString> materialNames;
          stream >> materialNames;

          QSettings* settings = Alt::settings();
          settings->beginGroup("global");
          settings->beginGroup("materials");
          settings->remove("");

          for(QHash<int, QString>::ConstIterator i = materialNames.begin(); i != materialNames.end(); ++i){
               settings->setValue(QString::number(i.key()), i.value());
          }

          settings->endGroup();
          settings->endGroup();
          delete settings;

          refreshMaterialNames();
          return true;
     }

     const QHash<QString, MapPlugin*>& Application::mapPlugins() {
          return m_mapPluginCache;
     }

     int Application::materialId(const QString& materialName) {
          return m_materialNameToIdHash.value(materialName, -1);
     }

     const QHash<QString, int>& Application::materialIds() {
          return m_materialNameToIdHash;
     }

     QString Application::materialName(int materialId) {
          return m_materialIdToNameHash.value(materialId, "unknown");
     }

     const QHash<int, QString>& Application::materialNames() {
          return m_materialIdToNameHash;
     }


     const QDir& Application::pluginDir() {
          return m_pluginDir;
     }

     const QObjectList& Application::pluginObjects() {
          return m_pluginObjectCache;
     }

     QString Application::pluginPath() {
          return m_pluginDir.absolutePath();
     }

     const QList<Plugin*>& Application::plugins() {
          return m_pluginCache;
     }

     void Application::refreshMaterialNames() {
          m_materialIdToNameHash.clear();
          m_materialNameToIdHash.clear();

          QSettings* settings = Alt::settings();
          settings->beginGroup("global");
          settings->beginGroup("materials");
          foreach (QString materialId, settings->allKeys()) {
               m_materialIdToNameHash.insert(materialId.toInt(), settings->value(materialId).toString());
               m_materialNameToIdHash.insert(settings->value(materialId).toString(), materialId.toInt());
          }
          settings->endGroup();
          settings->endGroup();
          delete settings;
          emit qApp->materialNamesRefreshed();
          return;
     }

     //private
     void Application::buildPluginCache(){
          m_pluginCache.clear();
          m_pluginObjectCache.clear();
          m_mapPluginCache.clear();
          m_mapEditorPluginCache.clear();
          foreach(QString fileName, recursiveFileEntryList(Application::pluginDir())){
               QPluginLoader loader(fileName);
               Plugin* plugin = qobject_cast<Plugin*>(loader.instance());
               if(plugin){
                    plugin->m_filePath = fileName;
                    m_pluginCache += plugin;
                    m_pluginObjectCache += loader.instance();
                    MapPlugin* mapPlugin = qobject_cast<MapPlugin*>(loader.instance());
                    if(mapPlugin){
                         foreach(QString mapType, mapPlugin->mapTypes()){
                          m_mapPluginCache.insert(mapType, mapPlugin);
                         }
                    }
                    MapEditorPlugin* mapEditorPlugin = qobject_cast<MapEditorPlugin*>(loader.instance());
                    if(mapEditorPlugin){
                         foreach(QString mapType, mapEditorPlugin->supportedMapTypes()){
                              m_mapEditorPluginCache.insert(mapType, mapEditorPlugin);
                         }
                    }
               } else {
                    loader.unload();
               }
          }
          return;
     }
}

